#ifndef __RADIANCEPOINT__
#define __RADIANCEPOINT__

#include "Point3D.h"
#include "RGBColor.h"
#include "Normal.h"
#include <string>
#include <math.h>
#include "Ray.h"

enum DISTANCE_SURFACES
{
	HARMONIC_MEAN,
	MIN_RAY_LENGH
};

using std::string;

namespace Engine
{
	const float A_CONSTANT = 0.5;

	class RadiancePoint
	{
	public:
		Point3D m_position;
		Point3D hitPointNormal;
		float m_rayLength;
		RGBColor radiance;
		Ray m_collisionRay;
		string m_objectName;

		RadiancePoint()
		{
			m_position = Point3D(0);
			hitPointNormal = Point3D();
			m_rayLength = 0;
			radiance = RGBColor(0);
		}

		RadiancePoint(Point3D& p_position, Point3D& p_normal, Ray& p_collsionRay, string p_objectName = "")
		{
			m_position = p_position;
			hitPointNormal = p_normal;
			m_collisionRay = p_collsionRay;
			m_objectName = p_objectName;
		}

		RadiancePoint(Point3D& p_position, float& p_rayLength)
		{
			m_position = p_position;
			m_rayLength = p_rayLength;
		}
		std::string ToString()
		{

			return m_position.toString();
		}


		//for the peer to peer system
		void FromString(std::string& stringRepresentation)
		{
		
		}

		Point3D GetEpsillonHitPoint()
		{
			return m_position + kEpsilon * hitPointNormal;
		}

		//==============================================================

		//Get Weigth : Calculate the weight of  the currnet Point with regards another point
	
		float GetWeighting(RadiancePoint* p_radiancePoint)
		{
			
			float demA = ((m_position - p_radiancePoint->m_position).length() / m_rayLength);
			float demB = (sqrt(1 - DOT(hitPointNormal, p_radiancePoint->hitPointNormal)));
			float denominator = 1 / (demA + demB);
			return (denominator) - (1 / A_CONSTANT);
			//return denominator;
		}

		//===============================================================

		void ComputeRadiance(vector<ShadeRec>& radianceSamples)
		{
			for (int sampleIndex = 0; sampleIndex < radianceSamples.size(); sampleIndex++)
			{
				//RGBColor currentRadiance = radianceSamples[sampleIndex].
				radiance += radianceSamples[sampleIndex].myColor;
			}
		}
		
		//===============================================================

		void ComputeRadianceNHarmonivValue(vector<ShadeRec*>& p_ShadeRecList, double& p_minRayLengths)
		{
			double invRaySummation = 0.0f;
			double minRayLength = INT_MAX;
			for (int sampleIndex = 0; sampleIndex < p_ShadeRecList.size(); sampleIndex++)
			{
				if (p_ShadeRecList[sampleIndex]->hitAnObject)
				{
					double rayVectorLength = (p_ShadeRecList[sampleIndex]->localHitPoint - m_position).length();

					minRayLength = min(rayVectorLength, minRayLength);
					radiance = radiance + p_ShadeRecList[sampleIndex]->myColor;
				}
			}
			if (minRayLength != 0)
				//m_rayLength = p_ShadeRecList.size() / invRaySummation;
				m_rayLength = max(minRayLength, p_minRayLengths);
			else
				m_rayLength = kEpsilon;
			//average
			if (radiance.r != 0 && radiance.b != 0 && radiance.g != 0)
				radiance = radiance / (int)p_ShadeRecList.size();

		}


		//==============================================================


		bool CheckIfValidToPoint(RadiancePoint* currentSample)
		{
			return false;
		}

		//==============================================================

		void GetHarmonicValue(vector<ShadeRec>& p_ShadeRecList)
		{
			double invRaySummation = 0.0f;

			for (int sampleIndex = 0; sampleIndex < p_ShadeRecList.size(); sampleIndex++)
			{
				if (p_ShadeRecList[sampleIndex].hitAnObject)
				{
					double rayVectorLength = (p_ShadeRecList[sampleIndex].hitPoint - m_position).length();

					invRaySummation += (1 / rayVectorLength);
				}
			}

			float rayLength = p_ShadeRecList.size() / invRaySummation;

			m_rayLength = rayLength;
		}

		//===============================================================

		inline bool operator == (RadiancePoint* p_radiancePoint)
		{
			return m_position == p_radiancePoint->m_position;
		}

		inline bool operator == (Point3D& p_position)
		{
			return m_position == p_position;
		}

		~RadiancePoint()
		{

		}

	private:
	};

}

#endif // !__RADIANCEPOINT__
